-
-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
(Design) Formatted Parts #463
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good to see this. Many initial comments...
exploration/0003-formatted-parts.md
Outdated
Past examples have shown us that if we don't provide a formatter to parts, | ||
the string output will be re-parsed and re-processed by users. | ||
|
||
## Use-Cases |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These need more flesh.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aphillips This section has been expanded since this comment. Sufficiently?
exploration/0003-formatted-parts.md
Outdated
Each part should have at most one of `value` or `parts` defined; | ||
some may have none. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this make sense? Or would it be better to say:
Each part should have at most one of `value` or `parts` defined; | |
some may have none. | |
Each part MUST have either a `value` or `parts` defined. | |
A part MAY have a `value` that is the empty string. | |
A part MAY have a `parts` that is an empty list. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The suggestion would make the proposed MessageFallbackPart invalid, as it does not include either value
or parts
. It's conceivable for other parts to also exist which do not include either, such as open/close expressions without an annotation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would an empty value
or empty parts
satisfy that? Or a fallback could have a string expression? Empty strings don't result in the erroneous emission of the string null
😉
I understand that it would "break" the current definition: we should decide what the shapes should be and make consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For fallback when formatting to a string the {...}
make sense as a visual indicator, but for a formatted-parts consumer some different representation could be better. So including an explicit value
or parts
would be misleading.
For open/close, it doesn't make sense to define their explicit parts shapes in this spec, but for JS I have them as:
interface MessageMarkupPart {
type: 'open' | 'close';
source: string;
name: string;
value?: unknown;
options: { [key: string]: unknown };
}
There, the value
would be 'b'
for {b +html}
, but it would not be set for {+html.b}
. Setting it to an empty string would be misleading, as {+foo}
and {|| +foo}
could easily mean different things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In #463 (comment) I'm suggesting to define separate interfaces for single-valued and multi-valued parts. This could extend to fallback parts and markup, as well.
exploration/0003-formatted-parts.md
Outdated
|
||
When the resolution or formatting of a placeholder fails, | ||
it is represented in the output by MessageFallbackPart. | ||
No `value` is provided; when formatting to a string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we provide the fallback value? I think we have some text in the spec that allows implementations or functions to supply their own fallback.
Question: should a goal be that the string output of a message be equivalent to concatenating the string representation of its parts? Or at least that a test be that one can assemble the string output from the parts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we provide the fallback value? I think we have some text in the spec that allows implementations or functions to supply their own fallback.
Fallback customization is only available for syntax and data model errors, which default to �
. That would be used as the source
value here.
Question: should a goal be that the string output of a message be equivalent to concatenating the string representation of its parts? Or at least that a test be that one can assemble the string output from the parts?
I think the latter. With the current proposal, it's doable like this:
function stringifyParts(parts) {
let res = ''
for (let part of parts) {
if (part.type === 'fallback') res += `{${part.source}}`
else if ('value' in part) res += String(part.value)
else if ('parts' in part) {
for (let sub of part.parts) res += String(sub.value)
}
}
return res
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the parts are technically not formatted yet?
I can't get from the proposal what a sub.value
is.
Can it be a date?
exploration/0003-formatted-parts.md
Outdated
and in most cases it's presumed that the sub-part `value` would be a string. | ||
|
||
```ts | ||
interface MessageDateTimePart { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we define the "parts" so that they are generic rather than each type having its own special part type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's the MessageExpressionPart definition above, which these interfaces also match. The definitions here are giving more specificity about what e.g. :datetime
and :number
end up producing, i.e. that they have explicit type
identifiers and define parts
rather than value
.
exploration/0003-formatted-parts.md
Outdated
interface MessageDateTimePart { | ||
type: "datetime"; | ||
source: string; | ||
parts: Iterable<{ type: string; value: unknown }>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Notice in my example earlier in this review that I exposed the field name as a field in the parts. This becomes important when trying to decorate an Iterable whose contents shift around due to the locale/localized formatting. Dates have this feature (YMD, DMY, MDY). So do currency values (which may or may not have a decimal part, may have the symbol first or last, and may or may not have a space around the symbol). That's how the screen shots of currency values (from amazon.com and amazon.fr) get decorated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's the "type"
field here. I picked that rather than "name"
because it's used by the JS Intl formatters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was super non-obvious to me (hence this conversation!), particularly since the type
fields in the examples seemed to be focused on the "type" of formatter (datetime, number) rather than on the parts field within them. Admittedly, the MF2-level parts will be at the placeholder level. Interior parts are the problem of the formatter. But this was not at all clear and probably could use an example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make sure that it's explained in more detail in the spec PR, should this design doc be accepted.
Co-authored-by: Addison Phillips <[email protected]>
Just to note, progress on this design doc appears to be blocked by lack of review for the past two months. |
Co-authored-by: Tim Chevalier <[email protected]>
Pinging @mihnita: You have an action to review/comment on this doc. |
My main concern (also expressed in Seville) is that the proposed model is conceptually a tree
So from what I understand the Let's take an example: If I read this proposal correctly, the "November 27-December 7, 2023" would be described by an So the most useful part for people's use cases ("how can format the months differently") is left out. It also looks to me like the result can't be used as is, or serialized, as it is not yet "formatted" I can't format-to-parts server side and send the result to a client, for example. I looked at existing implementations of similar concepts in iOS / macOS, Android, ICU4C / ICU4J, Eclipse SWT. And all of them use "flat" structures: a text, with information about ranges. It is not a bad design in itself, standalone. But my main concerns are:
Do we want to go in that direction? I will document the existing implementations in a separate comment, for readability. |
macOS & iOS
The attributes are in an APIs to access:
|
AndroidAPI: to access the span info: Classes:
Example:
Spans supported: https://developer.android.com/reference/android/text/style/package-summary |
ICU4JClasses implementing it: Two ways to iterate:
ICU4CImplemented in Iteration: There is no equivalent of |
SWTAPIs: |
It contains an This could be used to create an attributed string just as much as it could be used to create a sequence of DOM nodes. What is important is that the various boundaries are accessible to the caller and in the correct order for the localized message. It's important to know where the placeables were, but then know inside the placeable where sub-field boundaries are. Conversely, an implementation of MF2 could return an attributed string describing the parts defined here (using attributes instead of objects)? |
exploration/0003-formatted-parts.md
Outdated
source: string; | ||
parts?: Iterable<{ type: string; value: unknown }>; | ||
value?: unknown; | ||
dir?: "ltr" | "rtl" | "auto"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is unclear to me why have dir
and locale
at this level.
We don't need a locale to format anything, because the parts should be already formatted.
The whole proposal is called "Formatted Parts"
The locale might be needed to render things.
Or to process the formatting result (fix grammatical agreements as a post-step, fix "a apple" to "an apple" (en) or "La abeille" => "L'abbeile" (fr), or to sentence case the result of "{item} is foo"
But then that is something that is needed for the whole collection of parts, not on MessageExpressionPart
only.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are needed to allow embedding content in a message that uses a different script or locale than the surrounding message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kind of clear what they can be used for.
But this info is on the MessageExpressionPart
, which comes from an expression.
And an expression can't create this info out of nothing, it is probably something we passed as a parameter.
So if I already know the info (because I passed it to the expression), having it on the MessageExpressionPart
is useless duplication.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider this message, intended for consumption by a text-to-speech system:
In French, the number {98 :number} is commonly expressed as {98 :number @locale=fr},
but in Belgium it's {98 :number @locale=fr-BE}.
How would you propose that the locale information is transmitted, if not as a field on the formatted parts?
[
{ type: 'text', value: 'In French, the number ' },
{ type: 'number', source: '|98|', parts: [{ type: 'integer', value: '98' }] },
{ type: 'text', value: ' is commonly expressed as ' },
{ type: 'number', source: '|98|', locale: 'fr', parts: [{ type: 'integer', value: '98' }] },
{ type: 'text', value: ", but in Belgium it's " },
{ type: 'number', source: '|98|', locale: 'fr-BE', parts: [{ type: 'integer', value: '98' }] },
{ type: 'text', value: '.' }
]
As context, the fr
number would be "quatre-vingt-dix-huit", while in fr-BE
it's "nonante-huit".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Language and direction are needed for placeholders because they represent values being inserted into the overall string. The language (locale) is used to ensure proper rendering and processing (such as line breaking, text transforms, or spell-checking). The direction is used to enable bidi isolation and get the direction of the substring correct.
The language and direction of a formatted part might not match that of the message due to resource fallback when looking up the message. Or because values passed in have different language or direction. (And we want bidi isolation even if the directions match!!!!)
Providing the fields in the formatted part structure allows the user to easily access the values, e.g. it makes it easy to do something like this, resulting in proper isolation of the formatted parts (not shown is decorating the parts separately):
var message = // whatever the host node is for the string
for (let part of formattedMessage.parts) {
var span = document.createElement('span');
span.lang = part.lang;
span.dir = part.dir;
span.appendChild(document.createTextNode(part.value));
message.appendChild(span);
}
That is done on portions of the message. The whole message also has a language (locale) and base paragraph direction.
Yes, but it looks like we are mixing the inputs with the outputs. Now I need to be able to look at the output and access these parts and sub-parts without
Yes, but that implementation would not be standard.
Does not need to know, but if the formatters return parts that are "compatible" with what MF2 returns that is good. If they "part id" is a string then I can do something css selection. Example:
The "attributes" overlap, same as bold / italic / other attributes overlap. But then I can iterate and I get something like this:
And I can do the "css selection"-like operations:
If we leave the sub-parts undefined it is more inconsistent in how I access the parts / subparts. |
exploration/formatted-parts.md
Outdated
interface MessageExpressionPart { | ||
type: string; | ||
source: string; | ||
parts?: Iterable<{ type: string; value: unknown; source?: string }>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's call this subparts
, items
, values
, chunks
, fragments
... anything but parts
, which we already use to describe the things returned by the formatToParts
API.
exploration/formatted-parts.md
Outdated
type: string; | ||
source: string; | ||
parts?: Iterable<{ type: string; value: unknown; source?: string }>; | ||
value?: unknown; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be more robust to describe the polymorphism of parts through generics rather than unknown
. Something like the following:
interface MessageSingleValuePart<T> {
type: string;
source: string;
value: T;
dir?: "ltr" | "rtl" | "auto";
locale?: string;
}
type MessageStringPart = MessageSingleValuePart<string>;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where in the spec would you propose we would be able to make use of this polymorphism?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean to use it as a language for describing the concepts introduced by this doc; MessageStringPart
is one example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added generic interfaces in line with your suggestion, but they're now referred to in the doc via comments, so that each part can retain its full definition in-place and a reader doesn't need to (but may) look up how they relate to each other. This also allows us to continue expressing how e.g. MessageFallbackPart explicitly does not have locale
and dir
fields, even if it does otherwise extend MessageSingleValuePart<"fallback", never>
.
Sorry, in vacation last week.
Sorry, but no. I listed several concerns, so satisfying one is not enough. Recap:
The "impedance" is a problem even if 1-3 can be represented with the above proposal, because the representation does not feel "natural" to a system that already uses attribute-style APIs. Such a system would have several options:
|
@mihnita Thanks for your comment. Do you have an alternate design approach to suggest? |
Co-authored-by: Stanisław Małolepszy <[email protected]>
Addressing each below.
The example message you provided could be represented like this. If there is an example of a real-world message with improperly nesting ranges that you could point at, I'd be happy to show how such could be represented as well.
Should the target not be an input for the date formatter? I mean, if an environment sometimes has its messages formatted for display on a screen and sometimes for TTS, would it not be reasonable to expect that this target is an input to the date formatter as well? If this distinction is made only after formatting, then I would presume that the MF2-internal date formatter that it's using is a custom one, which emits parts that include a Date object of some sort, along with a basket of formatting options. That's certainly doable with the proposed approach.
Could you clarify how this is a concern?
In case you missed it earlier, I've here provided an example |
I am not sure what's not clear here. One use case is that If the a result has objects ( Because to "stringify" an object I need to have access to it.
That is unnecessary with the attributed approach. Example: If the server generates: {
"msg": "Hello John!"
"attrs" : [
{"start":6, "end":10, "userId:personName"}
]
} then it is easy to send to the client, and I have all the info I need to render it, and format the name any way I want. If the server generates "parts": [
"text": "Hello John!"
"placeholder" : {
"type": "userId:personName"
value: unknown
}
] then I don't know how to send And if I somehow marshal it to the client then I have to maintain all that marshaling code (to keep in sync when server side implementation changes), breaks encapsulation (the client now has to know about all kind of new types), and makes dependencies more complex. |
In one of the example above we had
What if the locale was Arabic? So we technically the |
Another use case for MF2 is formatting in front-end environments that do not have such restrictions. For example, a seemingly simple message like
could have its To format that message, we cannot format it to a string because then we'd end up with Would you agree that the above is a reasonable use case for MF2? If so, do you agree that this unfortunately leaves In the scenario you present, there are obviously further constraints that must be accounted for. These constraints may then be applied by the specific MF2 implementation, the functions you're using, or by checks on the post-processing for formatted-parts output. These restrictions should not limit what may be done by a different user of MF2.
Then the corresponding formatted-parts results would be:
The formatted parts are fully formatted parts. |
@mihnita Are you satisfied by how each of your four concerns has been addressed? |
I don't think I see any of my concerns addressed. The only change I see is "Add MessageSingleValuePart & MessageMultiValuePart definitions; other…" Which splits the previous Then It does not solve the "one level tree" problem, unless we say that
So that means that in this case value is a string (or something that extends string). But in general "type" should not really be a type, is semantic, no? Then maybe we need a name better than You bring in React. And I get it.
Absolutely. To summarize the current status, as I understand it:
Because we have a bunch of
Still not sure how to represent alternate streams of text (for screen rendering vs TTS) In general most things can be "solved" just because there are unknown types that can store anything. But that way things get soo flexible that there is no point to have this in the spec anymore.
Still unclear. Except by saying "hey, the value V can be anything, even JSON or protobuf (or string).
There is no solution for this, except for MF2 format to parts to return an annotated string. |
Yes, an annotated string. And if this is a design document (as the title says) should at least document that option, compare with the current tree of unknown nodes, and explain why not do what everything else does. |
Still open: we don't need the Let me explain again, in a different way, maybe I can be more clear:
Somehow MF takes the parse tree, with input arguments, and generates parts. And the a. not generated from a bidi control character typed by a translator (see Eemeli's answer in a thread above) b. not an from an explicit placeholder Example: Result, something like this:
We already have info about the The only place where |
Speaking of bold: we have no parts for markdown. |
Good! So we agree that at the most generic level, a formatted part returned by an arbitrary MF2 implementation should have a shape conforming to And that's what this spec is specifying; the most generic possible definition within which all parts for all placeholders in all implementations must fit. So we need to leave a lot of space for the unknown, because we need to support all possible formatting targets (not just strings), while still explicitly defining what structure we can, and common fields such as Any implementation is of course allowed to be stricter than what's defined here in its outputs, and processors of formatted parts are allowed to specify that they require e.g. only string values, or assign other restrictions to what they're capable of dealing with. But these limitations are not universal, and do not apply to all cases where formatted parts are emitted or consumed.
The
Yes. Because we must allow for arbitrary values like React components to show up as values, we must allow for the possibility that those values are not primitive, but contain some internal structure.
This thing is not pointless. An emitter or consumer of formatted parts can be more specific than this spec in what it emits or consumes, allowing for interoperability. It also has a rather important point in being a prerequisite for our markup definition.
Could you expand that alternative from its current definition? Right now it just defines it as "Format to a string, but separately define metadata or other values." It seems like you think this should be expanded?
Presuming that you mean "markup", then you'll find those included in the proposed design of the doc linked to from #537. Unless you really do mean "markdown", in which case I don't know what you mean. |
What about this: have both an It can be beneficial even for something like React.
Since That way you can post-process the result and fix grammar, or do other operations. The value can indeed be a Span (in Android), but the string value is not contained in a Span. SpannableString string = new SpannableString("Text with underline span");
string.setSpan(new UnderlineSpan(), 10, 19, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); So the underline Span does not contain text. It can also alleviate (maybe not completely?) the "alternate text" problem. Which sometimes is not even text: And
Thanks. That's what I expected. But I see, it is addressed in a different place.
Now you are nitpicking on what my brain produces at 3 am :-) Yes, I meant markup, "Markdown is a lightweight markup language" (Wikipedia) |
This sounds like something that an implementation could choose to do, but I don't really see why it should be enforced in all cases for all users? As far as I know, practically all reasonable programming languages allow for arbitrary objects to define a way for their own stringification when they're coerced to a string. In both JS and Java, that can be done by implementing a custom What's the benefit or requiring all formatters to always perform such a stringification?
At least in the JS Intl implementation, a value like that without an explicitly recognised type will be formatted to a string by calling { type: 'unknown', source: '$button', value: button } This way, arbitrary input values can be accepted and "formatted" without the core implementation needing to understand them, or being passed functions supporting their handling. |
But then it is not following the spec.
Do you know about C and C++? There is not standard way for an object to stringify itself. Second, even programming languages that have standard ways to objects to stringify themselves, that is intended for debugging and for developer consumption, not for end user. Third, saying that objects can stringify themselves ignores one of my requirements to be able to easily move "across borders" between programming languages, or server/client. And if possible be consistent? I really tried to see if we can make this proposal work your way, with "a tree of nodes." Do you think that all 4 bullets listed #463 (comment) are reasonable? |
Yes, it would be. In case that was not implicitly clear (we're using TypeScript syntax, after all), I've added an explicit mention of the liberty to define additional fields.
My most sincere apologies. I thought Now, leaving the snark aside (which tbh I'd also appreciate from you), do you see how requiring an
To reflect, we are here talking about formatting a message containing a placeholder { type: 'unknown', source: '$button', value: button } where the My understanding of what you're asking for is that even in this case, that formatted part should include something like Using such an implementation (ignoring any We are here defining the most specific and minimal data types that still enable for something like
Wait, I thought we'd agreed that something like What you're asking for is possible with further restrictions on what's enabled by this proposal. It is perfectly valid for e.g. a server-side formatter to have stricter requirements for what it supports as input data than just "anything goes", and for it to make stricter guarantees about its outputs, as required for transmission across the wire, for instance. On the other hand, it is also perfectly valid for a formatter to not impose such restrictions on its inputs, and not to promise anything stricter than what's in this proposal. If we were to restrict this proposal to the requirements of "formatted parts must be transmissible as data", we could not support React components. And you said this earlier:
Could you now clarify what you meant by "Absolutely", because I took that to be agreement with my preceding statement?
My statements above do not contradict each other. In JS, anything can be coerced to a string with
I do not think I will be able to convince you. I think you've decided that the approach proposed here isn't good, and you refuse to ever admit being wrong. I find discussions with you challenging, and they have been literally (not figuratively) the most draining part of my work for the past few years. You refuse to discuss any one item at a time, pivoting to others when it seems like continuing on a track will lead to a conclusion you don't like, and then when returning you discard any previous developments not to your liking. Sure, this might be just me, but I much prefer having this dialogue written down, where it's recorded.
All your concerns are reasonable, but they should also be considered resolved by my preceding answers. Specifically, I've answered 1, 2, and 4 in #463 (comment) and 3 in #463 (comment). If those answers are not satisfactory, then please re-read them, and tell me specifically why or how they do not address the concerns you've previously expressed, or if they contain any parts that you do not understand. Our discussion since then seems to have been going around in circles. Also, I reiterate my earlier request to expand on the attributed string alternative in this proposal, in case you find it lacking, and should you wish for it to be seriously considered as an alternative. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Observing the ongoing discussion, I'd like to propose to descope formatted parts from 2.0.
I think we should still recommend that implementations provide an API returning parts, but re-reading the doc, I don't think there's a compelling case for why to define such API in any more detail than what's currently presented in the "Requirements" section.
In fact, it occurs to me that our written requirements are the recommendation for implementers:
- Define an iterable sequence of formatted part objects.
- Include metadata for each part, such as type, source, direction, and locale.
- Allow the representation of non-string values.
- Allow the representation of values that consist of an iterable sequence of formatted parts.
- Be able to represent each resolved value of a pattern with any number of formatted parts, including none.
- [Removed the last one, because it's the only one that's not a recommendation to implementers.]
We struggle because we want to be super-agnostic on one side, and on the other, we want to specify something concrete so that compliant implementations can be swapped and can communicate with each other. However, I note that runtime fungibility and IPC are not listed in the use-cases of this design. Are we solving the right thing?
If runtime interoperability is not a goal, what do we actually gain from defining this optional agnostic interface as part of the spec?
(chair hat on) @eemeli @mihnita I observe that the discussion is starting to get personal. That's usually sign that we should switch to something like a slack huddle or at least take a breather. I'll remind everyone on the thread to focus on technical arguments and not stray into speculation about motives, etc. (chair hat OFF) @stasm Thank you for your comment above. I think I observe the same thing. I'll note that this is a design document and the "alternatives considered" section is extremely thin. The alternative @mihnita prefers has a single (somewhat biased) sentence in it. In other words, this document is not really ready in light of our other designs, as it is more for a development container for a specific solution. I think the text is valuable. We should consider what our approach to format to parts should be, perhaps in Monday's call. |
This is pre-empting #458 and #461, but I figure they'll likely land before this.
The proposed design is close to that used by the JS polyfill, but there are some differences.